package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"time"

	"gopkg.in/ini.v1"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

//配置文件所在路径
var cfgPath = "config.ini"
var MqServerIP = "120.234.66.170"
var MqServerPort = 7285
var MqClientID = "go_mqtt_client"
var MqUsername = "emqx"
var MqPassword = "1234"
var MqSubTopic = "test_Topic"
var MqPubTopic = "test_Topic"
var MqKeepAlive = 60
var MqPubTime = 4
var MqUseTLS = "true"
var MqPubFile = "ca.crt"
var MqCrtFile = ""
var MqKeyFile = ""
var MqCashStart = uint64(0)

func GetTime() string {
	return time.Now().Format("06/01/02 15:04:05")
}

//
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf("%s topic: 【%s】 Received: %s\n", GetTime(), msg.Topic(), msg.Payload())
}

//初始化配置文件
func ConfigInit() {
	cfgFile, err := ini.Load(cfgPath)
	if err != nil {
		fmt.Printf("%s ConfigInit err:%s\n", GetTime(), err.Error())
		return
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqServerIP").String(); len(Tmp) > 0 {
		MqServerIP = Tmp
	}
	if Tmp, _ := cfgFile.Section("MQTT").Key("MqServerPort").Int(); Tmp > 0 {
		MqServerPort = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqClientID").String(); len(Tmp) > 0 {
		MqClientID = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqUsername").String(); len(Tmp) > 0 {
		MqUsername = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqPassword").String(); len(Tmp) > 0 {
		MqPassword = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqSubTopic").String(); len(Tmp) > 0 {
		MqSubTopic = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqPubTopic").String(); len(Tmp) > 0 {
		MqPubTopic = Tmp
	}
	if Tmp, _ := cfgFile.Section("MQTT").Key("MqKeepAlive").Int(); Tmp > 0 {
		MqKeepAlive = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqUseTLS").String(); len(Tmp) > 0 {
		MqUseTLS = Tmp
	}
	if Tmp := cfgFile.Section("MQTT").Key("MqPubFile").String(); len(Tmp) > 0 {
		MqPubFile = Tmp
	}
	MqCrtFile = cfgFile.Section("MQTT").Key("MqCrtFile").String()
	MqKeyFile = cfgFile.Section("MQTT").Key("MqKeyFile").String()

	if Tmp, _ := cfgFile.Section("CUSPUB").Key("MqPubTime").Int(); Tmp > 0 {
		MqPubTime = Tmp
	}
	MqCashStart, _ = cfgFile.Section("CUSPUB").Key("MqCashStart").Uint64()
	fmt.Printf("\nMqServerIP = [%s]\tMqServerPort = [%d]\nMqClientID = [%s]\tMqUsername = [%s]\tMqPassword = [%s]\nMqSubTopic = [%s]\tMqPubTopic = [%s]\tMqPubTime = [%d]\tMqKeepAlive = [%d]\nMqUseTLS = [%s]\tMqPubFile = [%s]\tMqCrtFile = [%s]\tMqKeyFile = [%s]\n\n",
		MqServerIP, MqServerPort, MqClientID, MqUsername, MqPassword, MqSubTopic, MqPubTopic, MqPubTime, MqKeepAlive, MqUseTLS, MqPubFile, MqCrtFile, MqKeyFile)
}

func NewTLSConfig() *tls.Config {
	// Create tls.Config with desired tls properties
	TLSConfig := &tls.Config{
		// ClientAuth = whether to request cert from server.
		// Since the server is set up for SSL, this happens
		// anyways.
		ClientAuth: tls.NoClientCert,
		// ClientCAs = certs used to validate client cert.
		ClientCAs: nil,
		// InsecureSkipVerify = verify that cert contents
		// match server. IP matches what is in cert etc.
		InsecureSkipVerify: true,
	}
	// Import trusted certificates from CAfile.pem.
	// Alternatively, manually add CA certificates to
	// default openssl CA bundle.
	certPool := x509.NewCertPool()
	pemCerts, err := ioutil.ReadFile(MqPubFile)
	if err == nil {
		certPool.AppendCertsFromPEM(pemCerts)
		// RootCAs = certs used to verify server cert.
		TLSConfig.RootCAs = certPool
	} else {
		fmt.Printf("%s ReadFile %s err:%s\n", GetTime(), MqPubFile, err.Error())
		return TLSConfig
	}
	fmt.Printf("%s ReadFile CA[%s] Success\n", GetTime(), MqPubFile)

	fmt.Printf("%s MqCrtFile == \"%s\" || MqKeyFile == \"%s\"\n", GetTime(), MqCrtFile, MqKeyFile)
	if len(MqCrtFile) < 3 || len(MqKeyFile) < 3 {
		return TLSConfig
	}

	// Import client certificate/key pair
	cert, err := tls.LoadX509KeyPair(MqCrtFile, MqKeyFile)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s ReadFile client [%s,%s] Success\n", GetTime(), MqCrtFile, MqKeyFile)

	// Just to print out the client certificate..
	cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		panic(err)
	}

	// Certificates = list of certs client sends to server.
	TLSConfig.Certificates = []tls.Certificate{cert}

	return TLSConfig
}

//参数识别
func Daemonize(args ...string) {
	sysType := runtime.GOOS
	//后台运行
	if sysType == "linux" {
		var arg []string
		if len(args) > 1 {
			arg = args[1:]
		}
		fmt.Println("run in backModed")
		cmd := exec.Command(args[0], arg...)
		cmd.Env = os.Environ()
		cmd.Start()
	} else if sysType == "windows" {

	}
}
func main() {
	/*
		-d 后台运行
	*/
	args := os.Args
	daemon := false
	for k, v := range args {
		if v == "-d" {
			daemon = true
			args[k] = ""
		} else if v == "restart" {
			daemon = true
			args[k] = ""
		}
	}
	if daemon {
		Daemonize(args...)
		return
	}
	ConfigInit()
	CashCount = MqCashStart
	CashVal = CashCount
	opts := mqtt.NewClientOptions()
	opts.SetClientID(MqClientID)
	opts.SetUsername(MqUsername)
	opts.SetPassword(MqPassword)
	opts.SetCleanSession(true)
	opts.SetKeepAlive(time.Duration(MqKeepAlive) * time.Second)
	//3 - MQTT 3.1  4 - MQTT 3.1.1
	opts.SetProtocolVersion(4)
	opts.SetDefaultPublishHandler(messagePubHandler)
	//opts.SetAutoReconnect(true)
	//opts.SetConnectRetry(true)
	if MqUseTLS == "true" {
		opts.AddBroker(fmt.Sprintf("tls://%s:%d", MqServerIP, MqServerPort))
		opts.SetTLSConfig(NewTLSConfig())
		// Illustrates customized TLS configuration prior to connection attempt
		//opts.OnConnectAttempt = func(broker *url.URL, tlsCfg *tls.Config) *tls.Config {
		//	cfg := tlsCfg.Clone()
		//	cfg.ServerName = broker.Hostname()
		//	return cfg
		//}
	} else {
		opts.AddBroker(fmt.Sprintf("tcp://%s:%d", MqServerIP, MqServerPort))
	}
	//连接成功事件
	opts.OnConnect = func(client mqtt.Client) {
		fmt.Printf("%s Connected server success\n", GetTime())
	}
	//连接断开事件
	opts.OnConnectionLost = func(client mqtt.Client, err error) {
		fmt.Printf("%s Connect lost: %v\n", GetTime(), err)
	}
	//重连事件
	opts.OnReconnecting = func(mqtt.Client, *mqtt.ClientOptions) {
		fmt.Printf("%s attempting to reconnect\n", GetTime())
	}
	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}
	sub(client, MqSubTopic)
	for {
		select {
		case <-time.After(time.Duration(MqPubTime) * time.Second):

		}
		publish(client, MqPubTopic, createMessage())
	}
	client.Disconnect(250)
}

//订阅消息
func sub(client mqtt.Client, topic string) {
	token := client.Subscribe(topic, 1, nil)
	token.Wait()
	fmt.Printf("%s Subscribed【%s】\n", GetTime(), topic)
}

type Content struct {
	BillNo           string `json:"BillNo"`
	BroadcastContent string `json:"broadcastContent"`
}
type Cash struct {
	Content  Content `json:"content"`
	MsgId    string  `json:"msgId"`
	SerialNo string  `json:"serialNo"`
	Time     string  `json:"time"`
}

var CashCount uint64
var CashVal uint64

//随机生成收款消息
func createMessage() string {
	TimeNow := time.Now()
	a := CashVal / 100
	b := CashVal % 100
	CashMessage := &Cash{
		Content: Content{
			BillNo:           fmt.Sprintf("103%s%021d", TimeNow.Format("20060102"), CashCount),
			BroadcastContent: fmt.Sprintf("%d.%02d元", a, b),
		},
		MsgId: "VOICE-MESSAGE",
		Time:  fmt.Sprintf("%d", TimeNow.UnixNano()/1e6),
	}
	CashMessage.SerialNo = CashMessage.Content.BillNo
	message, _ := json.Marshal(CashMessage)
	CashCount++
	CashVal++
	return string(message)
}

//发布消息
func publish(client mqtt.Client, topic string, text string) {
	fmt.Printf("%s publish【%s】%s\n", GetTime(), topic, text)
	token := client.Publish(topic, 1, false, text)
	token.Wait()
}
